package com.holly.unit.query;


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.holly.unit.core.constants.CommonConstant;
import com.holly.unit.core.enums.QueryRuleEnum;
import com.holly.unit.core.util.SqlInjectionUtil;
import com.holly.unit.core.util.StrKitUtil;
import com.holly.unit.query.api.QueryApi;
import com.holly.unit.query.api.constants.QueryConstants;
import com.holly.unit.query.api.enums.MatchTypeEnum;
import com.holly.unit.query.api.pojo.QueryCondition;
import com.holly.unit.query.api.util.QueryUtil;
import com.holly.unit.query.pojo.SysPermissionDataRuleModel;
import com.holly.unit.query.util.QueryBusiUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.util.NumberUtils;

import java.beans.PropertyDescriptor;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map;

/**
 * 类描述: 查询构建器
 *
 * @author fxr
 * @version 1.0.0
 * @date 2022/4/6 14:57
 */
@Slf4j
public class QueryGenerator implements QueryApi {

    @Override
    public <T> QueryWrapper<T> initQueryWrapper(T searchObj, Map<String, String[]> parameterMap) {
        long start = System.currentTimeMillis();
        QueryWrapper<T> queryWrapper = new QueryWrapper<T>();
        installMplus(queryWrapper, searchObj, parameterMap);
        log.debug("---查询条件构造器初始化完成,耗时:"+(System.currentTimeMillis()-start)+"毫秒----");
        return queryWrapper;
    }

    @Override
    public void installMplus(QueryWrapper<?> queryWrapper, Object searchObj, Map<String, String[]> parameterMap) {

        /*
		 * 注意:权限查询由前端配置数据规则 当一个人有多个所属部门时候 可以在规则配置包含条件 orgCode 包含 #{sys_org_code}
		但是不支持在自定义SQL中写orgCode in #{sys_org_code}
		当一个人只有一个部门 就直接配置等于条件: orgCode 等于 #{sys_org_code} 或者配置自定义SQL: orgCode = '#{sys_org_code}'
		*/

        //区间条件组装 模糊查询 高级查询组装 简单排序 权限查询
        PropertyDescriptor origDescriptors[] = PropertyUtils.getPropertyDescriptors(searchObj);
        Map<String,SysPermissionDataRuleModel> ruleMap = QueryBusiUtil.getRuleMap();

        //权限规则自定义SQL表达式
        for (String c : ruleMap.keySet()) {
            if(StrUtil.isNotEmpty(c) && c.startsWith(QueryConstants.SQL_RULES_COLUMN)){
                queryWrapper.and(i ->i.apply(QueryBusiUtil.getSqlRuleValue(ruleMap.get(c).getRuleValue())));
            }
        }

        String name, type;
        for (int i = 0; i < origDescriptors.length; i++) {
            //aliasName = origDescriptors[i].getName();  mybatis  不存在实体属性 不用处理别名的情况
            name = origDescriptors[i].getName();
            type = origDescriptors[i].getPropertyType().toString();
            try {
                if (QueryUtil.judgedIsUselessField(name)|| !PropertyUtils.isReadable(searchObj, name)) {
                    continue;
                }

                //数据权限查询
                if(ruleMap.containsKey(name)) {
                    QueryBusiUtil.addRuleToQueryWrapper(ruleMap.get(name), name, origDescriptors[i].getPropertyType(), queryWrapper);
                }

                // 添加 判断是否有区间值
                String endValue = null,beginValue = null;
                if (parameterMap != null && parameterMap.containsKey(name + QueryConstants.BEGIN)) {
                    beginValue = parameterMap.get(name + QueryConstants.BEGIN)[0].trim();
                    QueryUtil.addQueryByRule(queryWrapper, name, type, beginValue, QueryRuleEnum.GE);

                }
                if (parameterMap != null && parameterMap.containsKey(name + QueryConstants.END)) {
                    endValue = parameterMap.get(name + QueryConstants.END)[0].trim();
                    QueryUtil.addQueryByRule(queryWrapper, name, type, endValue, QueryRuleEnum.LE);
                }
                //多值查询
                if (parameterMap != null && parameterMap.containsKey(name + QueryConstants.MULTI)) {
                    endValue = parameterMap.get(name + QueryConstants.MULTI)[0].trim();
                    QueryUtil.addQueryByRule(queryWrapper, name.replace(QueryConstants.MULTI,""), type, endValue, QueryRuleEnum.IN);
                }

                //判断单值  参数带不同标识字符串 走不同的查询
                //TODO 这种前后带逗号的支持分割后模糊查询需要否 使多选字段的查询生效
                Object value = PropertyUtils.getSimpleProperty(searchObj, name);
                if (null != value && value.toString().startsWith(QueryConstants.COMMA) && value.toString().endsWith(QueryConstants.COMMA)) {
                    String multiLikeval = value.toString().replace(",,", QueryConstants.COMMA);
                    String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(QueryConstants.COMMA);
                    final String field = StrKitUtil.camelToUnderline(name);
                    if(vals.length>1) {
                        handleQueryWrapper(queryWrapper, vals, field);
                    }else {
                        queryWrapper.and(j -> j.like(field,vals[0]));
                    }
                }else {
                    //根据参数值带什么关键字符串判断走什么类型的查询
                    QueryRuleEnum rule = QueryUtil.convert2Rule(value);
                    value = QueryUtil.replaceValue(rule,value);
                    // add -begin 添加判断为字符串时设为全模糊查询
                    //if( (rule==null || QueryRuleEnum.EQ.equals(rule)) && "class java.lang.String".equals(type)) {
                    // 可以设置左右模糊或全模糊，因人而异
                    //rule = QueryRuleEnum.LIKE;
                    //}
                    // add -end 添加判断为字符串时设为全模糊查询
                    QueryUtil.addEasyQuery(queryWrapper, name, rule, value);
                }

            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        // 排序逻辑 处理
        doMultiFieldsOrder(queryWrapper, parameterMap);

        //高级查询
        doSuperQuery(queryWrapper, parameterMap);

    }

    // TODO 校验【return j】
    private void handleQueryWrapper(QueryWrapper<?> queryWrapper, String[] vals, String field) {
        queryWrapper.and(j -> {
            j = j.like(field, vals[0]);
            for (int k = 1; k < vals.length; k++) {
                j = j.or().like(field, vals[k]);
            }
            //return j;
        });


    }

    @Override
    public void doMultiFieldsOrder(QueryWrapper<?> queryWrapper, Map<String, String[]> parameterMap) {

        String column=null,order=null;
        if(parameterMap!=null&& parameterMap.containsKey(QueryConstants.ORDER_COLUMN)) {
            column = parameterMap.get(QueryConstants.ORDER_COLUMN)[0];
        }
        if(parameterMap!=null&& parameterMap.containsKey(QueryConstants.ORDER_TYPE)) {
            order = parameterMap.get(QueryConstants.ORDER_TYPE)[0];
        }
        log.debug("排序规则>>列:"+column+",排序方式:"+order);
        if (StrUtil.isNotEmpty(column) && StrUtil.isNotEmpty(order)) {
            //字典字段，去掉字典翻译文本后缀
            if(column.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) {
                column = column.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX));
            }
            //SQL注入check
            SqlInjectionUtil.filterContent(column);

            if (order.toUpperCase().indexOf(QueryConstants.ORDER_TYPE_ASC)>=0) {
                queryWrapper.orderByAsc(StrKitUtil.camelToUnderline(column));
            } else {
                queryWrapper.orderByDesc(StrKitUtil.camelToUnderline(column));
            }
        }

    }

    @Override
    public void doSuperQuery(QueryWrapper<?> queryWrapper, Map<String, String[]> parameterMap) {

        if(parameterMap!=null&& parameterMap.containsKey(QueryConstants.SUPER_QUERY_PARAMS)){
            String superQueryParams = parameterMap.get(QueryConstants.SUPER_QUERY_PARAMS)[0];
            String superQueryMatchType = parameterMap.get(QueryConstants.SUPER_QUERY_MATCH_TYPE) != null ? parameterMap.get(QueryConstants.SUPER_QUERY_MATCH_TYPE)[0] : MatchTypeEnum.AND.getValue();
            MatchTypeEnum matchType = MatchTypeEnum.getByValue(superQueryMatchType);
            // update-begin--Author:sunjianlei  Date:20200325 for：高级查询的条件要用括号括起来，防止和用户的其他条件冲突 -------
            try {
                superQueryParams = URLDecoder.decode(superQueryParams, "UTF-8");
                List<QueryCondition> conditions = JSON.parseArray(superQueryParams, QueryCondition.class);
                if (conditions == null || conditions.size() == 0) {
                    return;
                }
                log.info("---高级查询参数-->" + conditions.toString());
                queryWrapper.and(andWrapper -> {
                    for (int i = 0; i < conditions.size(); i++) {
                        QueryCondition rule = conditions.get(i);
                        if (StrUtil.isNotEmpty(rule.getField())
                                && StrUtil.isNotEmpty(rule.getRule())
                                && StrUtil.isNotEmpty(rule.getVal())) {

                            log.debug("SuperQuery ==> " + rule.toString());
                            QueryUtil.addEasyQuery(andWrapper, rule.getField(), QueryRuleEnum.getByValue(rule.getRule()), rule.getVal());

                            // 如果拼接方式是OR，就拼接OR
                            if (MatchTypeEnum.OR == matchType && i < (conditions.size() - 1)) {
                                andWrapper.or();
                            }
                        }
                    }
                    return;
                });
            } catch (UnsupportedEncodingException e) {
                log.error("--高级查询参数转码失败：" + superQueryParams, e);
            } catch (Exception e) {
                log.error("--高级查询拼接失败：" + e.getMessage());
                e.printStackTrace();
            }
            // update-end--Author:sunjianlei  Date:20200325 for：高级查询的条件要用括号括起来，防止和用户的其他条件冲突 -------
        }
    }

    @Override
    public String getSingleQueryConditionSql(String field, String alias, Object value, boolean isString) {
        if (value == null) {
            return "";
        }
        field =  alias+StrKitUtil.camelToUnderline(field);
        QueryRuleEnum rule = QueryUtil.convert2Rule(value);
        return QueryBusiUtil.getSingleSqlByRule(rule, field, value, isString);
    }

    @Override
    public String installAuthJdbc(Class<?> clazz) {

        StringBuffer sb = new StringBuffer();
        //权限查询
        Map<String,SysPermissionDataRuleModel> ruleMap = QueryBusiUtil.getRuleMap();
        PropertyDescriptor origDescriptors[] = PropertyUtils.getPropertyDescriptors(clazz);
        String sql_and = " and ";
        for (String c : ruleMap.keySet()) {
            if(StrUtil.isNotEmpty(c) && c.startsWith(QueryConstants.SQL_RULES_COLUMN)){
                sb.append(sql_and+QueryBusiUtil.getSqlRuleValue(ruleMap.get(c).getRuleValue()));
            }
        }
        String name;
        for (int i = 0; i < origDescriptors.length; i++) {
            name = origDescriptors[i].getName();
            if (QueryUtil.judgedIsUselessField(name)) {
                continue;
            }
            if(ruleMap.containsKey(name)) {
                SysPermissionDataRuleModel dataRule = ruleMap.get(name);
                QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
                Class propType = origDescriptors[i].getPropertyType();
                boolean isString = propType.equals(String.class);
                Object value;
                if(isString) {
                    value = QueryBusiUtil.converRuleValue(dataRule.getRuleValue());
                }else {
                    value = NumberUtils.parseNumber(dataRule.getRuleValue(),propType);
                }
                String filedSql = QueryBusiUtil.getSingleSqlByRule(rule, StrKitUtil.camelToUnderline(name), value,isString);
                sb.append(sql_and+filedSql);
            }
        }
        log.info("query auth sql is:"+sb.toString());
        return sb.toString();
    }

    @Override
    public void installAuthMplus(QueryWrapper<?> queryWrapper, Class<?> clazz) {

        //权限查询
        Map<String,SysPermissionDataRuleModel> ruleMap = QueryBusiUtil.getRuleMap();
        PropertyDescriptor origDescriptors[] = PropertyUtils.getPropertyDescriptors(clazz);
        for (String c : ruleMap.keySet()) {
            if(StrUtil.isNotEmpty(c) && c.startsWith(QueryConstants.SQL_RULES_COLUMN)){
                queryWrapper.and(i ->i.apply(QueryBusiUtil.getSqlRuleValue(ruleMap.get(c).getRuleValue())));
            }
        }
        String name;
        for (int i = 0; i < origDescriptors.length; i++) {
            name = origDescriptors[i].getName();
            if (QueryUtil.judgedIsUselessField(name)) {
                continue;
            }
            if(ruleMap.containsKey(name)) {
                QueryBusiUtil.addRuleToQueryWrapper(ruleMap.get(name), name, origDescriptors[i].getPropertyType(), queryWrapper);
            }
        }

    }

    @Override
    public String convertSystemVariables(String sql) {

        return QueryBusiUtil.getSqlRuleValue(sql);
    }

    @Override
    public String getAllConfigAuth() {
        StringBuffer sb = new StringBuffer();
        //权限查询
        Map<String,SysPermissionDataRuleModel> ruleMap = QueryBusiUtil.getRuleMap();
        String sql_and = " and ";
        for (String c : ruleMap.keySet()) {
            SysPermissionDataRuleModel dataRule = ruleMap.get(c);
            String ruleValue = dataRule.getRuleValue();
            if(StrUtil.isEmpty(ruleValue)){
                continue;
            }
            if(StrUtil.isNotEmpty(c) && c.startsWith(QueryConstants.SQL_RULES_COLUMN)){
                sb.append(sql_and+QueryBusiUtil.getSqlRuleValue(ruleValue));
            }else{
                boolean isString  = false;
                ruleValue = ruleValue.trim();
                if(ruleValue.startsWith("'") && ruleValue.endsWith("'")){
                    isString = true;
                    ruleValue = ruleValue.substring(1,ruleValue.length()-1);
                }
                QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
                String value = QueryBusiUtil.converRuleValue(ruleValue);
                String filedSql = QueryBusiUtil.getSingleSqlByRule(rule, c, value,isString);
                sb.append(sql_and+filedSql);
            }
        }
        log.info("query auth sql is = "+sb.toString());
        return sb.toString();
    }

    @Override
    public String getSqlRuleValue(String sqlRule) {
        return QueryBusiUtil.getSqlRuleValue(sqlRule);
    }
}
